<?php
/**
* Model基类
*
* 本程序主要用来取得在Model中使用的一些基本方法和基本变量
* 
* @category   Model
* @package    Base
* @copyright  Copyright (c) ink工作室 (http://www.inkphp.com)
* @author     张旭刚
* @version    v1.0 beta
*/
class Model{
    protected $db;
    public $name;
    public $table;
    public $pageSize = 10;
    public $mem;
    private $querySql;
    public function __construct($db = null, $name = null) {
        $this->name = $name;
        $this->db = $db;
        if(__USE_MEMCACHE__){
            $this->mem = $this->X('Mem');
        }
        $this->setTable();
    }
    
    /**
     * @method 获取当前Model对应的表 
     */
    protected function setTable(){
        $this->table = __DB_PREFIX__.strtolower($this->name);
    }
    
    /**
     * @method 设置视图
     * @param type $viewSql
     */
    protected function createView($viewSql = null){
        $sql = 'SHOW TABLES WHERE Tables_in_'.__DB_NAME__.'=\''.$this->table.'\'';
        $data = $this->query($sql);
        if(count($data) <= 0){
            if(empty($viewSql)){
                die('the_view_not_exists');
            }
            $this->execSql($viewSql);
        }
    }
    
    public function setPageSize($pageSize){
        $this->pageSize = $pageSize;
    }
    
    /**
     * @method 运行Sql并直接返回数据
     * @param string $sql Sql语句
     * @return array 返回结果集 
     */
    protected function query($sql){
        $this->querySql = $sql;
        $result = $this->db->Execute($sql);
        while($arr = $this->db->FetchRow()){
            $data[] = $this->arrayUnique($arr);
        }
        return $data;
    }
    
    /**
     * @method 运行Sql并返回是否运行成功
     * @return boolean or object
     */
    public function execSql($sql){
        $this->querySql = $sql;
        if(is_object($this->db->Execute($sql))){
            return true;
        }else{
            return false;
        }
    }
    
    /**
     * @method 对查询所得的数据集进行处理，去掉所有数字键项
     * @param array $data 要处理的查询结果
     * @return array 处理之后的结果
     */
    private function arrayUnique($data){
        foreach((array)$data as $key => $value){
            if(is_numeric($key)){
                unset($data[$key]);
            }
        }
        return $data;
    }
    
    /**
     * @method 根据条件取得数据表中有符合条件的数据的数量
     * @param array $map 查询条件
     * @return int 查询结果数量值
     */
    public function getCount($map = null){
        $where = $this->getWhere($map);
        $sql = 'SELECT count(1) as `count` FROM '.$this->table.' WHERE 1 '.$where;
        $data = $this->query($sql);
        return $data[0]['count'];
    }
    
    /**
     * @method 取得当前数据表中的单挑数据，根据给定的id
     * @param array $field 要显示的字段
     * @param string $id 查询的id值
     * @return array 返回查询得到的数据
     */
    public function getOne($field, $id, $pk = null){
        if($pk == null){
            $pk = $this->getPK();
        }
        $d = $this->getOneFromDB($field, $pk, $id);
        return $d[0];
    }
    
    /**
     * @method 形如key-value数据库的单条数据进行json解析
     * @param string $key 要查询的key
     * @return array 返回的结果
     */
    public function getOneValue($key){
        $field = array('_value');
        $data = $this->getOne($field, $key);
        $value = $data['_value'];
        $d = (array)json_decode($value);
        return $d;
    }
    
    /**
     * @method 从数据库中获得单条数据
     * @param array $field 要查询的字段
     * @param string $pk 主键
     * @param string $id 主键值
     * @return array 
     */
    private function getOneFromDB($field, $pk, $id){
        $where = 'WHERE `'.$pk.'` = \''.$id.'\'';
        $fields = $this->getFields($field);
        $sql = 'SELECT '.$fields.' FROM '.$this->table.' '.$where;
        $d = $this->query($sql);
        return $d;
    }
    
    /**
     * @method 根据数组形成INSERT Sql语句
     * @param array $data 数据
     * @return string sql
     */
    private function getInsertSql($data){
        $keys = array_keys($data);
        $sql = 'INSERT INTO '.$this->table.'(`'.implode('`,`', $keys).'`) VALUES(\''.implode('\',\'',$data).'\')';
        return $sql;
    }
    
    /**
     * @method 向表中插入数组
     * @param array $data 
     * @return boolean or object 
     */
    public function insert($data){
        $sql = $this->getInsertSql($data);
        if($this->execSql($sql)){
            $insertId = $this->InsertId(); 
            if(!$insertId){
                return true;
            }
            return $insertId;
        }else{
            return false;
        }
    }
    
    public function InsertId(){
        $status = $this->db->Insert_ID();
        return $status;
    }
    
    /**
     * @method 根据条件获得符合条件的所有数据的列表
     * @param array $fields 要查询的字段
     * @param array or string $maps 查询条件
     * @param string $order 排序条件
     * @param string $limit 查询条数
     * @return array 查询结果
     */
    public function getAll($fields = null, $maps = null, $order = null, $limit = null){
        $field = $this->getFields($fields);
        if(!empty($maps)){
            $where = $this->getWhere($maps);
        }
        if(!empty($order)){
            $order = ' ORDER BY '.$order;
        }
        if(!empty($limit)){
            $limit = ' LIMIT '.$limit;
        }
        $sql = 'SELECT '.$field.' FROM '.$this->table.' WHERE 1 '.$where.$order.$limit;
        $d = $this->query($sql);
        return $d;
    }
    
    /**
     * @method 根据数组取得要查询的字段字符串
     * @param array $fields
     * @return string 要查询的字段星形成的字符串
     */
    private function getFields($fields){
        if(!is_array($fields)){
            if(empty($fields) || $fields == '*'){
                $str = '*';
            }else{
                $str = $fields;
            }
        }else{
            $str = '`'.implode('`,`',$fields).'`';
        }
        return $str;
    }
    
    /**
     * @method 根据查询条件进行分页查询 - 也就是通用分页方法
     * @param array $fields 要查询的字段
     * @param int $page 当前页码
     * @param array $maps 查询条件
     * @param string $pageType 分页显示方式
     * @param string $order 排序方式
     * @return array 查询结果和分页字符串
     */
    public function getPageList($fields, $page = 1, $maps = null, $pageMode = 'goPage', $order = null,$pageType = 'Number'){
        $field = $this->getFields($fields);
        if(!empty($maps)){
            $where = $this->getWhere($maps);
        }
        $pageData = $this->getPageParams($page, $where);
        $pageData['pageMode'] = $pageMode;
        if($page <= $pageData['maxPage']){
            $start = $pageData['start'];
            $limit = ' LIMIT '.$start.','.$this->pageSize;
            if(!empty($order)){
                $order = ' ORDER BY '.$order.' ';
            }
            $sql = 'SELECT '.$field.' FROM '.$this->table.' WHERE 1'.$where.$order.$limit;
            $rows = $this->query($sql);
            $pageData['count'] = count($rows);
            if($pageData['maxPage'] > 1){
                $pageStr = $this->getPageStr($pageType,$pageData);
            }else{
                $pageStr = '';
            }
        }else{
            $rows = null;
            $pageStr = '';
        }
        return array('rows' => $rows, 'pageStr' => $pageStr);
    }
    
    /**
     * @method 根据传过来的条件分析取得查询Sql语句的Where部分
     * @param array $maps 查询条件
     * @return string where部分
     */
    private function getWhere($maps){
        if(!is_array($maps)){
            return $maps;
        }else{
            foreach($maps as $key => $value){
                $str .= ' and `'.$key.'`=\''.$value.'\'';
            }
        }
        return $str;
    }
    
    /**
     * @method 取得分页所需要的参数
     * @param int $page 当前页码
     * @param string $where 查询条件
     * @return array 分页所需要的参数
     */
    private function getPageParams($page,$where){
        $sql = 'SELECT count(1) as Num FROM '.$this->table.' WHERE 1'.$where;
        $rows = $this->query($sql);
        $num = $rows[0]['Num'];
        $maxPage = ceil($num / $this->pageSize);
        if($page > $maxPage){
            $page = $maxPage;
        }
        if($page < 1){
            $page = 1;
        }
        $start = ($page-1)* $this->pageSize;
        return array('num' => $num, 'page' => $page, 'maxPage' => $maxPage, 'start' => $start);
    }
    
    /**
     * @method 分页显示字符串（html）
     * @param string $pageType 分页显示方式
     * @param array $pageData 分页所需数据
     * @return string 分页显示的方式
     */
    private function getPageStr($pageType = 'Normal', $pageData){
        $funcName = 'getPage'.$pageType;
        return $this->X('Page', $pageData)->$funcName();
    }
    
    /**
     * @method 根据数组取得Update语句
     * @param array $data 数据
     * @return string 返回Update语句
     */
    private function getUpdateSql($data){
        $pk = $this->getPK();
        $where = ' WHERE `'.$pk.'` = \''.$data[$pk].'\'';
        unset($data[$pk]);
        $keys = array_keys($data);
        foreach((array)$keys as $k => $v){
            $str .= '`'.$v.'`=\''.$data[$v].'\',';
        }
        $str = substr($str, 0, strlen($str)-1);
        $sql = 'UPDATE '.$this->table.' SET '.$str.$where;
        return $sql;
    }
    
    /**
     * @method 根据数组更新数据库的某条数据
     * @param array $data 数组
     * @return boolean or object 返回是否成功更新
     */
    public function update($data){
        $sql = $this->getUpdateSql($data);
        return $this->execSql($sql);
    }
    
    public function updateMaps($data, $maps){
        $where = $this->getWhere($maps);
        $keys = array_keys($data);
        foreach((array)$keys as $k => $v){
            $str .= '`'.$v.'`=\''.$data[$v].'\',';
        }
        $str = substr($str, 0, strlen($str)-1);
        $sql = 'UPDATE '.$this->table.' SET '.$str.$where;
        return $this->execSql($sql);
    }
    
    /**
     * @method 根据给的的主键值删除符合条件的数据
     * @param array $ids 主键值列表
     * @return boolean or object 返回成功与否
     */
    public function delete($ids){
        $pk = $this->getPK();
        $sql = 'DELETE FROM '.$this->table.' WHERE `'.$pk.'` in(\''.implode('\',\'',$ids).'\')';
        return $this->execSql($sql);
    }
    
    public function deleteMaps($maps){
        $where = $this->getWhere($maps);
        $sql = 'DELETE FROM '.$this->table.' WHERE 1 '.$where;
        return $this->execSql($sql);
    }
    
    /**
     * @method 取得表的主键
     * @return string 返回主键
     */
    protected function getPK(){
        if(__USE_MEMCACHE__){
            $mem_key = $this->table.'_pk';
            $pk = $this->mem->get($mem_key);
            if(empty($pk)){
                $pk = $this->getPKFromDB();
                if(!empty($pk)){
                    $this->mem->set($mem_key, $pk);
                }
            }
        }else{
            $pk = $this->getPKFromDB();
        }
        return $pk;
    }
    
    /**
     * @method 从数据表中获取主键
     * @return string 返回主键
     */
    private function getPKFromDB(){
        $sql = 'SHOW INDEX FROM '.$this->table.' WHERE Key_name=\'PRIMARY\'';
        $data = $this->query($sql);
        $pk = $data[0]['Column_name'];
        return $pk;
    }
    
    /**
     * @method 取得第三方插件实例的方法
     * @param string $name 插件名称
     * @param array $params 实例化时需要的参数
     * @return object 返回三方插件的实例 
     */
    protected function X($name = null,$params = null){
        $xpath = __LIB__.'/'.$name.'.class.php';
        if(!file_exists($xpath)){
            return 'lib_file_is_not_exist:'.$xpath;
        }else{
            include_once($xpath);
            if(!class_exists($name)){
                return 'lib_class_is_not_define:'.$name;
            }else{
                if(!empty($params)){
                    return new $name($params);
                }else{
                    return new $name();
                }
            }
        }
    }
    
    public function printSql(){
        echo $this->querySql;
        exit;
    }
}